package category

import (
	"context"

	"shop-manager/internal/do"

	"github.com/samber/lo"
	"gorm.io/gorm"
)

func (c *CategoryRepo) CreateCategory(ctx context.Context, oneData *do.Category) (int, error) {
	if err := c.db.WithContext(ctx).Create(&oneData).Error; err != nil {
		return 0, err
	}
	return oneData.ID, nil
}

func (c *CategoryRepo) UpdateCategory(ctx context.Context, oneData *do.Category) error {
	origin := &do.Category{}
	if err := c.db.WithContext(ctx).First(origin, oneData.ID).Error; err != nil {
		return err
	}

	origin.ClassID = oneData.ClassID
	origin.LevelID = oneData.LevelID
	origin.BrandID = oneData.BrandID

	return c.db.WithContext(ctx).Save(&origin).Error
}

func (c *CategoryRepo) DeleteCategory(ctx context.Context, id int) error {
	return c.db.WithContext(ctx).Delete(&do.Category{}, id).Error
}

type ListCategoryParam struct {
	Page     int
	PageSize int

	IDs           []int // category ids
	CategoryTypes []do.CategoryType
	Search        string // 模糊搜索

	// 排序, 格式为 field [ASC/DESC], ...
	Sorts string

	// 是否返回额外的字段(如Name等)
	ReturnExtraFields bool
}

// 分页查询
func (repo *CategoryRepo) ListCategory(ctx context.Context, param ListCategoryParam) (listData []*do.Category, total int64, err error) {
	if param.Page <= 0 {
		param.Page = 1
	}
	if param.PageSize == 0 { // -1 means no limit
		param.PageSize = DefaultPageSize
	}
	if param.PageSize > MaxPageSize {
		param.PageSize = MaxPageSize
	}

	err = repo.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		tx = tx.Model(&do.Category{}).Table("category AS c")

		if param.Search != "" || len(param.CategoryTypes) > 0 {
			sql := `INNER JOIN category_meta m ON (
				(c.class_id = m.id AND m.category_type = ?) OR
				(c.level_id = m.id AND m.category_type = ?) OR
				(c.brand_id = m.id AND m.category_type = ?)) `
			args := []interface{}{do.CategoryType_Class, do.CategoryType_Level, do.CategoryType_Brand}
			tx.Joins(sql, args...)

			if param.Search != "" {
				tx.Where("m.name like ?", "%"+param.Search+"%")
			}
			if len(param.CategoryTypes) > 0 {
				tx.Where("m.category_type in ?", param.CategoryTypes)
			}
		}
		if len(param.IDs) > 0 {
			tx.Where("c.id in ?", param.IDs)
		}

		err = tx.Count(&total).Error
		if err != nil {
			return err
		}

		if param.Sorts != "" {
			tx = tx.Order(param.Sorts)
		}
		err = tx.Model(&do.Category{}).Find(&listData).Count(&total).Error
		if err != nil {
			return err
		}

		offset := (param.Page - 1) * param.PageSize
		err = tx.
			Offset(offset).
			Limit(param.PageSize).
			Find(&listData).Error
		if err != nil {
			return err
		}

		return err
	})

	if param.ReturnExtraFields && len(listData) > 0 {
		metaIds := lo.Uniq(lo.FlatMap(listData, func(c *do.Category, _ int) []int {
			return []int{c.ClassID, c.LevelID, c.BrandID}
		}))

		metaMap, err := repo.GetCategoryMetaMap(ctx, ListCategoryMetaParam{
			PageSize: do.PageSizeNoLimit,
			IDs:      metaIds,
		})
		if err != nil {
			return nil, 0, err
		}

		for _, category := range listData {
			if c, ok := metaMap[category.ClassID]; ok {
				category.ClassName = c.Name
			}
			if l, ok := metaMap[category.LevelID]; ok {
				category.LevelName = l.Name
			}
			if b, ok := metaMap[category.BrandID]; ok {
				category.BrandName = b.Name
			}
		}
	}

	return listData, total, err
}

// GetCategoryMap
func (repo *CategoryRepo) GetCategoryMap(ctx context.Context, param ListCategoryParam) (map[int]*do.Category, error) {
	listData, _, err := repo.ListCategory(ctx, param)
	if err != nil {
		return nil, err
	}
	categoryMap := make(map[int]*do.Category)
	for _, item := range listData {
		categoryMap[item.ID] = item
	}
	return categoryMap, nil
}

func (repo *CategoryRepo) GetCategoryByKey(ctx context.Context, classId, levelId, brandId int) (*do.Category, error) {
	oneData := &do.Category{}
	err := repo.db.WithContext(ctx).Where("class_id = ? AND level_id = ? AND brand_id = ?", classId, levelId, brandId).First(&oneData).Error
	if err != nil {
		return nil, err
	}
	return oneData, nil
}
